package middleware

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"ia/apps/admin/middleware/jwt"
	"ia/common/icasbin"
	"ia/common/support"
	"ia/common/support/global"
	"strconv"
	"strings"
	"time"

	"github.com/kataras/golog"
	"github.com/kataras/iris/v12"
)

func ServeHTTP(ctx iris.Context) {
	var (
		err  error
		path = ctx.Path()
		ok   bool
	)
	golog.Infof("==> host=%s, method=%s, path=%s", ctx.Host(), ctx.Method(), path)

	if func(path string) bool {
		if strings.Contains(path, "/static") {
			return true
		}
		for _, v := range global.GConfig.App.Own.IgnoreURLs {
			if path == v {
				return true
			}
		}
		return false
	}(ctx.Path()) {
		ctx.Next()
		return
	}

	// 检查回话
	if err = jwt.ServeHTTP(ctx); err != nil {
		support.Error_(ctx, iris.StatusForbidden, global.CodeUnauthorized, "中间件token检验失败,错误:%s", err)
		return
	}

	if ok, err = icasbin.GCasbin.Enforce(strconv.Itoa(ctx.Values().GetIntDefault(global.Uid, 0)),
		global.GConfig.App.Own.Tenant, ctx.Path(), ctx.Method(), ".*"); err != nil {
		support.InternalServerError(ctx, global.CodeFailure)
		return
	}
	if !ok {
		support.Error(ctx, iris.StatusForbidden, global.CodeForbidden)
		return
	}

	// 校验参数
	// if err = sign(ctx); err != nil {
	// 	golog.Errorf("API签名校验失败:%s", err)
	// 	support.Error(ctx, iris.StatusBadRequest, global.CodeParamsError)
	// 	return
	// }

	// Pass to real API
	ctx.Next()
}

// 请求的API参数+时间戳+盐 进行MD5算法加密，加密后的数据就是本次请求的签名signature，
// 服务端接收到请求后以同样的算法得到签名，并跟当前的签名进行比对，如果不一样，说明参数被更改过，直接返回错误标识。
// 签名机制保证了数据不会被篡改
// 参考 https://www.jianshu.com/p/10fedb2e8f3f
const salt = "XXX"

func sign(ctx iris.Context) error {
	var (
		timestamp int64
		err       error
		body      []byte
		urlParams []byte
		buffer    bytes.Buffer

		srcSign string = ctx.GetHeader("sign")
		dstSign string
	)
	// 客户端请求头传过来的timestamp
	if timestamp, err = strconv.ParseInt(ctx.GetHeader("ts"), 10, 64); err != nil {
		return err
	}
	// TODO 时间不能超过1分钟
	if time.Now().Sub(time.Unix(timestamp/int64(time.Microsecond), 0)).Minutes() > 1 {
		return errors.New("API请求时间超过一分钟")
	}

	if strings.EqualFold("POST", ctx.Method()) || strings.EqualFold("PUT", ctx.Method()) || strings.EqualFold("DELETE", ctx.Method()) {
		if body, err = ctx.GetBody(); err != nil {
			return err
		}
		buffer.Write(body)
		golog.Infof("method=%s, body=%s", ctx.Method(), body)
	} else if strings.EqualFold("GET", ctx.Method()) {
		if urlParams, err = json.Marshal(ctx.URLParams()); err != nil {
			return err
		}
		buffer.Write(urlParams)
		golog.Infof("method=%s, body=%s", ctx.Method(), urlParams)
	} else {
		return errors.New("API签名,错误的请求方法")
	}
	golog.Infof("=>参数=%v,时间戳=%v,盐=%v", buffer.String(), timestamp, salt)

	buffer.WriteString(strconv.FormatInt(timestamp, 10))
	buffer.WriteString(salt)

	golog.Infof("==>>md5的原文=%v", buffer.String())
	dstSign = fmt.Sprintf("%x", md5.Sum(buffer.Bytes()))
	golog.Infof("=====>>请求=%s", srcSign)
	golog.Infof("=====>>服务=%s", dstSign)
	golog.Infof("=====>>校验=%t", strings.EqualFold(srcSign, dstSign))
	golog.Info("***********************************************")
	if !strings.EqualFold(srcSign, dstSign) {
		return errors.New("API签名不一致")
	}
	return nil
}
